package dgraph

import (
	"context"
	"encoding/json"
	"flag"
	"gitee.com/wuzheng0709/backend-gopkg/infrastructure/pkg/gin/log"
	"github.com/dgraph-io/dgo/v210"
	"github.com/dgraph-io/dgo/v210/protos/api"
	"google.golang.org/grpc"
	// "strings"
	"fmt"
	"time"
)

type CancelFunc func()

var (
	dgraph = flag.String("d", "39.100.125.246:9087", "Dgraph server address")
)

func GetDgraphClient() (*dgo.Dgraph, CancelFunc) {
	conn, err := grpc.Dial(*dgraph, grpc.WithInsecure())
	if err != nil {
		log.Fatal("While trying to dial gRPC")
	}

	dc := api.NewDgraphClient(conn)
	dg := dgo.NewDgraphClient(dc)
	// ctx := context.Background()

	return dg, func() {
		if err := conn.Close(); err != nil {
			log.Info("Error while closing connection", err)
		}
	}
}

func GetSchema(schema, query string) {
	dg, cancel := GetDgraphClient()
	defer cancel()

	op := &api.Operation{}
	op.Schema = `
		name: string @index(exact) .
		status: bool .
		loc: geo .
		dob: datetime .
	`
	op.Schema = schema
	ctx := context.Background()
	err := dg.Alter(ctx, op)
	if err != nil {
		log.Fatal(err)
	}

	// Ask for the type of name and age.
	//query: `schema(pred: [name, age]) {type}`
	resp, err := dg.NewTxn().Query(ctx, query)
	if err != nil {
		log.Fatal(err)
	}

	// resp.Json contains the schema query response.
	fmt.Println(string(resp.Json))
	// Output: {"schema":[{"predicate":"age","type":"int"},{"predicate":"name","type":"string"}]}
}

type loc struct {
	Type   string    `json:"type,omitempty"`
	Coords []float64 `json:"coordinates,omitempty"`
}

// If omitempty is not set, then edges with empty values (0 for int/float, "" for string, false
// for bool) would be created for values not specified explicitly.

type Person struct {
	Uid       string     `json:"uid,omitempty"`
	Name      string     `json:"name,omitempty"`
	UpdatedAt *time.Time `json:"updatedAt,omitempty"`
	Status    bool       `json:"status,omitempty"`
	Friend    []Person   `json:"friend,omitempty"`
	Location  loc        `json:"loc,omitempty"`
	DType     []string   `json:"dgraph.type,omitempty"`
}

func SetObject() {
	dg, cancel := GetDgraphClient()
	defer cancel()

	dob := time.Date(1980, 01, 01, 23, 0, 0, 0, time.UTC)
	// While setting an object if a struct has a Uid then its properties in the graph are updated
	// else a new node is created.
	// In the example below new nodes for Alice, Bob and Charlie and school are created (since they
	// don't have a Uid).
	p := Person{
		Uid:   "_:10",
		Name:  "吴峥",
		DType: []string{"Person"},
		Location: loc{
			Type:   "Point",
			Coords: []float64{1.1, 2},
		},
		Status:    true,
		UpdatedAt: &dob,
		Friend: []Person{{
			Uid:    "_:231",
			Name:   "冉旭东",
			Status: true,
			DType:  []string{"Person"},
		}, {
			Uid:    "_:5366",
			Name:   "张文轩",
			Status: true,
			DType:  []string{"Person"},
		}},
	}

	op := &api.Operation{}
	op.Schema = `
		name: string @index(exact) .
		loc: geo .
		updatedAt: datetime .
		friend: [uid] .
		type: string .
		coords: float .
		status: bool .
		type Person {
			name: string
			status: bool
			friend: [Person]
			loc: Loc
		}

		type Institution {
			name: string
		}

		type Loc {
			type: string
			coords: float
		}
	`

	ctx := context.Background()
	if err := dg.Alter(ctx, op); err != nil {
		log.Fatal(err)
	}

	mu := &api.Mutation{
		CommitNow: true,
	}
	pb, err := json.Marshal(p)
	if err != nil {
		log.Fatal(err)
	}

	mu.SetJson = pb
	response, err := dg.NewTxn().Mutate(ctx, mu)
	if err != nil {
		log.Fatal(err)
	}

	// Assigned uids for nodes which were created would be returned in the response.Uids map.
	variables := map[string]string{"$id1": response.Uids["10"]}
	log.Info(variables, response.Uids)
	q := `query Me($id1: string){
		me(func: uid($id1)) {
			name
			updatedAt
			loc
			status
			dgraph.type
			friend @filter(eq(name, "张文轩")){
				name
				dgraph.type
			}
		}
	}`

	resp, err := dg.NewTxn().QueryWithVars(ctx, q, variables)
	if err != nil {
		log.Fatal(err)
	}

	type Root struct {
		Me []Person `json:"me"`
	}

	var r Root
	err = json.Unmarshal(resp.Json, &r)
	if err != nil {
		log.Fatal(err)
	}

	out, _ := json.MarshalIndent(r, "", "\t")
	fmt.Printf("%s\n", out)
	// Output: {
	// 	"me": [
	// 		{
	// 			"name": "Alice",
	// 			"age": 26,
	// 			"dob": "1980-01-01T23:00:00Z",
	// 			"married": true,
	// 			"raw_bytes": "cmF3X2J5dGVz",
	// 			"friend": [
	// 				{
	// 					"name": "Bob",
	// 					"age": 24,
	// 					"loc": {},
	// 					"dgraph.type": [
	// 						"Person"
	// 					]
	// 				}
	// 			],
	// 			"loc": {
	// 				"type": "Point",
	// 				"coordinates": [
	// 					1.1,
	// 					2
	// 				]
	// 			},
	// 			"school": [
	// 				{
	// 					"name": "Crown Public School",
	// 					"dgraph.type": [
	// 						"Institution"
	// 					]
	// 				}
	// 			],
	// 			"dgraph.type": [
	// 				"Person"
	// 			]
	// 		}
	// 	]
	// }
}
